/* Decode the call shape table, registering call shapes with the VM,
 * and producing a mapping table that is used to translate decoded SEND
 * operations to use VM-specific call shape slots.
 */ 
KewTranslationTable *kew_decode_call_shape_table(KewBitReader *bit_reader)
{
    unsigned i;
    KewTranslationTable *table = kew_new(KewTranslationTable);
    table->size = kew_decode_unsigned(bit_reader);
    table->translate = kew_array_new(unsigned, table->size);
    
    for (i = 0; i < table->size; ++i) {
        unsigned number_of_args;
        unsigned multi_arg_set;
        unsigned number_of_results;
        unsigned multi_result_set;

        number_of_args = kew_decode_unsigned(bit_reader);
        if (number_of_args > 32)
            g_error("Internal limit of 32 arguments reached");
        multi_arg_set = kew_bit_reader_read_n(bit_reader, number_of_args);

        number_of_results = kew_decode_unsigned(bit_reader);
        if (number_of_results > 32)
            g_error("Internal limit of 32 results reached");
        multi_result_set = kew_bit_reader_read_n(bit_reader, number_of_results);
        
        table->translate[i] = kew_call_shape_register(number_of_args, multi_arg_set, number_of_results, multi_result_set);
    }
    
    return table;
}

/* Decode the selector table, registering selectors with the VM, and producing a mapping
 * table that is used to translate decoded SEND operations to use VM-specific
 * selector slots.
 * This could benefit from a nice prefix compression or something similar.
 * Also, it would be good if it supported Unicode.
 */
KewTranslationTable *kew_decode_selector_table(KewBitReader *bit_reader)
{
    int i;
    KewTranslationTable *table = kew_new(KewTranslationTable);
    table->size = kew_decode_unsigned(bit_reader);
    table->translate = kew_array_new(int, table->size);
    
    for (i = 0; i < table->size; ++i) {
        unsigned selector_length = kew_decode_unsigned(bit_reader);

        /* Hmm, not sure alloca is right here - this forces all the strings
         * into a single stack frame */
        char *selector = g_alloca(char, selector_length);

        for (j = 0; j < selector_length; ++j)
            selector[j] = kew_bit_reader_read_n(bit_reader, 8); /* FIXME: nicer ways to read Unicode here! */
        table->translate[i] = kew_selector_register(selector);
    }
    
    return table;
}

KewCodeReference *kew_decode_reference_type(KewBitReader *bit_reader)
{
    KewCodeReferenceType translate[4] = {ARGUMENT_REF, CAPTURE_REF, SELF_REF, BUILT_IN_REF};
    if (! kew_bit_reader_read(bit_reader)
        return EXPRESSION_REF;
    else
        return translate[kew_bit_reader_read_n(bit_reader, 2)];
}

KewCodeInstructionType kew_decode_instruction_type(KewBitReader *bit_reader)
{
    if (! kew_bit_reader_read(bit_reader))
        return SEND;
    else if (! kew_bit_reader_read(bit_reader))
        return BLOCK_INSTANTIATION;
    else if (! kew_bit_reader_read(bit_reader))
        return OBJECT_INSTANTIATION;
    if (! kew_bit_reader_read(bit_reader))
        return kew_bit_reader_read(bit_reader) ? INTEGER_CONSTANT : STRING_CONSTANT;
    else {
        switch (kew_bit_reader_read_n(bit_reader, 2)) {
            case 0: return BEGIN_OBJECT_INSTANTIATION;
            case 1: return END_OBJECT_INSTANTIATION;
            case 2: return FLOAT_CONSTANT;
            case 3: g_error("Reserved instruction bitcode found - malicious code? FIXME: exception here");
        }
    }
}

/* Though this deals with unsigned numbers, we keep the decoded number inside 31 bits as a safety
 * measure to prevent malicious code using wrap-around to access slots at negative offsets.
 */
unsigned kew_decode_unsigned(KewBitReader *bit_reader)
{
    unsigned result = 0;
    while (kew_bit_reader_read(bit_reader)) {
        result <<= 1;
        if (result & 0x80000000)
           g_error("Malformed bitcode - attempts to form >31 bit slot index (FIXME: raise exception)");
        result |= kew_bit_reader_read(bit_reader);
    }
    return result;
}

unsigned kew_decode_expression_slot(KewBitReader *bit_reader, unsigned current_expression_slot)
{
    unsigned slot = current_expression_slot - 1 - kew_decode_unsigned(bit_reader);
    if (slot >= current_expression_slot)
        g_error("Malformed bitcode - accesses undefined expression slot (FIXME: this should be an exception)");
    return slot;
}

KewCodeReference *kew_decode_reference(KewBitReader *bit_reader, unsigned current_expr_slot, unsigned number_of_args, unsigned number_of_captures)
{
    KewCodeReference *ref = kew_alloc(KewCodeReference); /* FIXME - what's the allocator func called again?! */
    ref->type = kew_decode_reference_type(bit_reader);
    switch (ref->type) {
        case SELF_REF: break;

        case EXPRESSION_REF:
            ref->slot = kew_decode_expression_slot(bit_reader, current_expr_slot);
            break;

        case ARGUMENT_REF:
            ref->slot = kew_decode_unsigned(bit_reader);
            if (ref->slot >= number_of_args)
                g_error("Malicious code - attempting to access beyond last argument (FIXME: exception)");
            break;

        case CAPTURE_REF:
            ref->slot = kew_decode_unsigned(bit_reader);
            if (ref->slot >= number_of_captures)
                g_error("Malicious code - attempting to access beyond last captured value (FIXME: exception)");
            break;
            
        case BUILT_IN_REF:
            ref->slot = kew_decode_unsigned(bit_reader);
            if (ref->slot > KEW_LAST_BUILT_IN)
                g_error("Malicious code - attempting to access non-existent built-in value (FIXME: exception)");
            break;
    }
    return ref;
}

KewCodeBlock *kew_decode_block(KewBitReader *bit_reader,
                               KewTranslationTable *selector_translation_table,
                               KewTranslationTable *call_shape_translation_table)
{
    KewCodeInstructions *instructions;
    KewCodeBlock *block;
    unsigned current_expression_slot = 0;
    unsigned i,j;

    block = kew_new(KewCodeBlock);
    block->call_shape = decode_unsigned(bit_reader);
    block->number_of_capture_slots = decode_unsigned(bit_reader);
    block->number_of_instructions = decode_unsigned(bit_reader);
    block->instructions = kew_new_array(KewCodeInstructions, number_of_instructions);
    
    for (i = 0; i < block->number_of_instructions; ++i) {
        KewCodeInstructionType instruction_type = decode_operation(bit_reader);
        block->instructions[i].type = instruction_type;
        
        switch (instruction_type) {
        
        case SEND:
            receiver_slot = decode_expression_slot(bit_reader, current_expression_slot);
            selector = kew_translation_table_translate(selector_translation_table, decode_unsigned(bit_reader), "Malformed bitcode - refers to a selector that it hasn't defined");
            call_shape = kew_translation_table_translate(call_shape_translation_table, decode_unsigned(bit_reader), "Malformed bitcode - refers to a call shape that it hasn't defined");            
            number_of_arguments = kew_call_shape_number_of_arguments(call_shape);
            argument_slots = kew_new_array(unsigned, number_of_arguments);

            for (j = 0; j < number_of_arguments; ++j)
                argument_slots[j] = decode_expression_slot(bit_reader, current_expression_slot);
            
            block->instructions[i].data.send.receiver_slot = receiver_slot;
            block->instructions[i].data.send.selector = selector;
            block->instructions[i].data.send.call_shape = call_shape;
            block->instructions[i].data.send.argument_slots = argument_slots;
            
            current_expression_slot += kew_call_shape_number_of_results(call_shape);
            break;

        case BLOCK_INSTANTIATION:
            g_error("(FIXME)");
            break;

        case OBJECT_INSTANTIATION:
            g_error("(FIXME)");
            break;

        case STRING_CONSTANT:
            /* FIXME: deal with Unicode here */
            break;

        case INTEGER_CONSTANT:
            /* FIXME: one day deal with large integers here */
            break;

        case BEGIN_OBJECT_INSTANTIATION:
            break;

        case END_OBJECT_INSTANTIATION:
            break;

        case FLOAT_CONSTANT:
            break;

        default:
            g_error("Unknown or reserved bitcode instruction (FIXME: this should be an exception)");
        }
    }
    
    return block;
}

void kew_decode_bitcode(KewBitReader *bit_reader)
{
    /* FIXME: check magic and version number of stream */
    /* FIXME: partial implementation */
    KewTranslationTable *selector_translation_table = decode_selector_table(bit_reader);
    KewTranslationTable *call_shape_translation_table = decode_call_shape_table(bit_reader);
    /* ... */
    KewCodeBlock *block = decode_block(bit_reader, selector_translation_table, call_shape_translation_table);
}

